#include <keller30drv.h>
#include <stdlib.h>

#define PACKAGE "keller30reader"
#define VERSION "0.1"

struct termio tnew,tsaved;


int serial_setup(char *device, int speed)
{
	int ttyfd;
	int port_speed;

	switch(speed) {
		case 9600:
			port_speed = B9600;
			break;
		case 19200:
			port_speed = B19200;
			break;
		case 115200:
			port_speed = B115200;
			break;
		default:
			fprintf(stderr, "%s: Invalid serial speed\n", PACKAGE);
			return -1;
	}

	/* Open device for Read and Write */
	ttyfd = open(device,O_RDWR);
	if (ttyfd < 0) {
		fprintf(stderr, "%s: Error: Unable to open %s\n", PACKAGE, device);
		return ttyfd; // Unable to open device
	}
	
	/* Configure serial device */
	ioctl(ttyfd,TCGETA,&tsaved); // backup existing configuration
	tnew.c_line = 0;
	tnew.c_oflag &=~ OPOST;
	tnew.c_lflag &=~ ICANON;
	tnew.c_cflag &=~ CBAUD;
	tnew.c_cflag |=  port_speed;               // baud rate 9600
	tnew.c_cflag &=~ CSIZE;
	tnew.c_cflag |= CS8;                       // 8 bits RTU
	tnew.c_cflag |= CREAD;                     // enable read
	tnew.c_cflag |= CLOCAL;                    // ignore modem lines
	tnew.c_cc[VMIN] = 0;
	tnew.c_cc[VTIME] = 0;
	ioctl(ttyfd,TCSETA,&tnew);

	return ttyfd;
}

void print_help() 
{
	printf("Usage: %s -f num [-c channel]\n\n"
	       "Options:\n\t"
	       "-f   function requested from the device (supported: 48, 73, 74), check manual\n\t"
	       "-c   channel id (one of: CH0, P1, P2, T, TOB1, TOB2)\n\t"
	       "-a   address of the slave device (default: 1)\n\t"
	       "-d   serial device path (default: /dev/ttyS0)\n\t"
	       "-s   serial port speed (default: 9600)\n\t"
	       "-t   maximum time spent waiting for slave device response in seconds (default: 5)\n\t"
	       "-h   this help\n\t"
	       "-v   print %s version\n\t"
	       "-V   print installed driver's version\n\n", PACKAGE, PACKAGE);
}

void print_version() 
{
	printf("%s version: %s\n", PACKAGE, VERSION);
}

void print_driver_version() 
{
	printf("%s driver version: %s\n", KDRV_NAME, get_driver_version());
}
 

int main(int argc, char **argv)
{

	int ttyfd;
	int   int_value   = 0;
	float float_value = 0;	
	int error = 0;
	int opt;
	struct f48_response_s f48_response;
   
	/* Default options */
	int timeout = 5;
	int slave_addr = 1;
	char *serial_device_path = "/dev/ttyS0";
	int serial_speed  = 9600;

	/* Required options */
	int function_number = 0;
	char *channel = 0;

	if(argc == 1) {
		print_help();
		return 0;
	}

	/* Get supplied options */
	opterr = 0;
	while((opt = getopt(argc, argv, "hvVf:c:t:a:d:s:")) != -1) {
	  switch(opt) {

	   case 'h':
		print_help();
		return 0;

	   case 'v':
		print_version();
		return 0;

	   case 'V':
		print_driver_version();
		return 0;

	   case 'f':
		// Set function number
		function_number = atoi(optarg);
		break;

	   case 'c':
		// Set channel id
		channel = optarg;
		break;

	   case 't':
		// Set timeout
		timeout = atoi(optarg);
		break;

	   case 'a':
		// Set slave address
		slave_addr = atoi(optarg);
		break;

	   case 'd':
		// Set serial device path
		serial_device_path = optarg;
		break;

	   case 's':
		// Set port speed
		serial_speed = atoi(optarg);
		break;

	   case '?':
		if ( (optopt == 'f') || (optopt == 'c') || (optopt == 't') || (optopt == 'a') || (optopt == 'd') || (optopt == 's') )
			fprintf(stderr, "%s: Option %c needs a value\n\n", PACKAGE, optopt);
		else
			fprintf(stderr, "%s: No such option: %c\n\n", PACKAGE, optopt);

		print_help();
		return 1;

	   default:
		 break;
		 
	   }
	}

	/* Check if provided function is supported */
	if (function_number == 0) {
		fprintf(stderr, "%s: No function provided\n", PACKAGE);
		return 1;
	}

	/* Set serial */
	ttyfd = serial_setup(serial_device_path, serial_speed);
	if (ttyfd < 0) {
		goto ERROR;
	}

	/* Perfrom appropriate function */
	switch (function_number) {
		case 48:
			/* Function 48 : Initialise and releas  */

			/* Call the function */
			f48_initialise(ttyfd, slave_addr, timeout, &f48_response, &error);
			if (error == KDRV_SUCCESS) {
				fprintf(stdout, "Device initialised:\nId: %d.%d\nFirmware: %d-%d\nBuffer Size: %d\nStat: %d\n", 
				        f48_response.dev_class, f48_response.dev_group, f48_response.year, f48_response.week,
					f48_response.buf_size, f48_response.stat);
			}
			else {
				fprintf(stderr, "Could not initialise the device\n");
				goto ERROR;
			}
			break;

		case 73:
			/* Function 73 :  Read value of a channel (floating point) */

			/* Verify that channel has been provided and is supported */
			if ( (channel == NULL) || (get_channel_id(channel) < 0) ) {
				fprintf(stderr, "%s: Invalid channel (check usage)\n", PACKAGE);
				goto ERROR;
			}
			/* Call the function */
			float_value = f73_read_channel(ttyfd, slave_addr, timeout, get_channel_id(channel), &error);
			if (error == KDRV_SUCCESS) {
				fprintf(stdout, "%s = %f\n", channel, float_value);
			}
			else {
				if (error == KDRV_EXCEPTION_ERROR) {
					if ((int)float_value == 32) {
					// if initialisation required call the init function
						f48_initialise(ttyfd, slave_addr, timeout, &f48_response, &error);
					}
				}
				goto ERROR;
			}
			break;

		case 74:
			/* Function 74 : Read value of a channel (integer) */

			/* Verify that channel id has been provided and is supported */
			if (channel < 0) {
				fprintf(stderr, "%s: Invalid channel (check usage)\n", PACKAGE);
				goto ERROR;	
			}
			/* Call the function */
			int_value = f74_read_channel(ttyfd, slave_addr, timeout, get_channel_id(channel), &error);
			if (error == KDRV_SUCCESS) {
				fprintf(stdout, "%s = %d\n", channel, int_value);
			}
			else {
				if (error == KDRV_EXCEPTION_ERROR) {
					if (int_value == 32) {
					// if initialisation required call the init function
						f48_initialise(ttyfd, slave_addr, timeout, &f48_response, &error);
					}
				}
				goto ERROR;
			}
			break;
		
		default:
			/* Unsupported function requested */
			fprintf(stderr, "%s: Unsupported function\n", PACKAGE);
			break;
	}


	ioctl(ttyfd,TCSETA,&tsaved);
	close(ttyfd);
	return 0;

ERROR:
	ioctl(ttyfd,TCSETA,&tsaved);
	close(ttyfd);
	if (error != 0) {
		fprintf(stderr, "%s: Error: %s\n", PACKAGE, get_kdrv_error(error));
		return error;
	}
	return 1;

}

